package com.gonsin.easycount.model;

import com.gonsin.easycount.enums.Caches;
import com.gonsin.easycount.plugin.OuterCache;
import com.gonsin.easycount.plugin.OuterLock;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import static com.google.common.collect.Iterables.isEmpty;

/**
 * 数值获取器
 * 调用方根据不同的情况获取不同的值
 */
public class CountGetter {

    private OuterLock lock;

    /**
     * 本次计数关系的标题
     */
    private String topic;

    private OuterCache cache;

    /**
     * 本次结果输出是否由手动flush触发。
     */
    private boolean byFlush;

    public CountGetter(String topic, OuterLock lock, OuterCache cache, boolean byFlush) {
        this.lock = lock;
        this.topic = topic;
        this.cache = cache;
        this.byFlush = byFlush;
    }

    /**
     * 各种未来准备的操作
     */
    // 是否需要计算总票数
    private boolean needSum;
    // 是否需要计算票数
    private Set<String> needSumOptions = new HashSet<>();
    // 是否需要获取新增人票
    private boolean needNewUserInfos;
    // 是否需要获取holder里的内容
    private boolean needHolder;
    // holder的类型
    private Class holderClazz;
    // 额外数据的类型
    private Class extraClazz;
    // 是否获取所有人员选项
    private boolean needAllUserInfos;

    /**
     * 将要查询总票数
     */
    public CountGetter needSum(){
        this.needSum = true;
        return this;
    }


    /**
     * 将要查询总票数
     */
    public CountGetter needSumOptions(String option){
        this.needSumOptions.add(option);
        return this;
    }


    /**
     * 将要查询holder
     */
    public CountGetter needHolder(Class holderClazz){
        this.needHolder = true;
        this.holderClazz = holderClazz;
        return this;
    }


//    /**
//     * 将要查询总票数
//     */
//    public CountGetter needSumUserInfos(String userInfo){
//        this.needSumUserInfos.add(userInfo);
//        return this;
//    }


    /**
     * 是否获取所有人员选项
     */
    public CountGetter needAllUserInfos(Class extraClazz){
        this.needAllUserInfos = true;
        this.extraClazz = extraClazz;
        return this;
    }

    /**
     * 将要查询总票数
     */
    public CountGetter needNewUserInfos(Class extraClazz){
        this.needNewUserInfos = true;
        this.extraClazz = extraClazz;
        return this;
    }


    public boolean isByFlush() {
        return byFlush;
    }

    /**
     * 获取查询数据
     */
    public CountResult get(){
        CountResult result = new CountResult();
        result.setByFlush(byFlush);
        lock.readLock(topic);
        try {

            if(needSum){
                result.setSum(cache.sizeOfHash(Caches.OPTION_HASH_MAP(topic)));
            }

            if(!isEmpty(needSumOptions)){
                for(String option : needSumOptions){

                    Long count = cache.sizeOfSet(Caches.USER_OPTION_SET(topic, option));
                    count = count == null ? 0 : count;
                    result.addSumOption(option, count);
                }
            }

//            if(!CollectionUtils.isEmpty(needSumUserInfos)){
//                for(String userInfo : needSumUserInfos){
//                    long count = cache.sizeOfHash(Caches.OPTION_HASH_MAP(topic));
//                    result.addSumUserInfos(userInfo, count);
//                }
//            }

            if(needNewUserInfos){
                if(Void.class == extraClazz || null == extraClazz){
//                    result.setNewUserInfos(cache.getList(Caches.NEW_USER_LIST(topic), Void.class));
                    ParameterizedType type = sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.make(UserInfoOption.class, new Type[]{Void.class}, null);
                    result.setNewUserInfos(cache.getList(Caches.NEW_USER_LIST(topic), type));
                    cache.cleanSet(Caches.NEW_USER_LIST(topic));
                } else {
//                    ParameterizedType type = new ParameterizedTypeImpl(new Type[]{UserInfoOption.class, extraClazz}, UserInfoOption.class, extraClazz);
                    ParameterizedType type = sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.make(UserInfoOption.class, new Type[]{extraClazz}, null);
                    result.setNewUserInfos(cache.getList(Caches.NEW_USER_LIST(topic), type));
                    cache.cleanSet(Caches.NEW_USER_LIST(topic));
                }
            }

            if(needHolder){
                if(holderClazz != null && holderClazz != Void.class){
                    result.setHolder(cache.get(Caches.HOLDER(topic), holderClazz));
                }
//                String typeName = cache.get(Caches.HOLDER_TYPE(topic), holderClazz);
//                try {
//                } catch (ClassNotFoundException e) {
//                    throw new RuntimeException(e);
//                }

            }

            if(needAllUserInfos){
                if(Void.class == extraClazz || null == extraClazz){
                    Map<String, UserInfoOption<?>> totalOptions = cache.getHash(Caches.OPTION_HASH_MAP(topic), Void.class);
                    result.setAllUserInfos(new HashSet<>(totalOptions.values()));
                } else {
//                    ParameterizedType type = new ParameterizedTypeImpl(
//                            new Type[]{UserInfoOption.class, extraClazz},
//                            UserInfoOption.class,
//                            extraClazz);
                    ParameterizedType type = sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl.make(UserInfoOption.class, new Type[]{extraClazz}, null);

                    Map<String, UserInfoOption<?>> totalOptions = cache.getHash(Caches.OPTION_HASH_MAP(topic), type);
                    result.setAllUserInfos(new HashSet<>(totalOptions.values()));
                }

            }

        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            lock.readUnlock(topic);
        }
        return result;
    }
}
